﻿#include "CommandsManager.h"
#include "../Modules/ModulesManager.h"
#include "../../Utils/Logger.h"

std::map<std::u8string, ICommand*> CommandMgr::NameTable{};
char8_t CommandMgr::prefix = u8'.';

void CommandMgr::initCommands() {
	Logger::LogC(LogGrade::info, u8"Commands Init!");
	[[maybe_unused]] auto lock = std::shared_lock(ModuleManager::moduleListMutex);
	constexpr size_t commandcount = getCommandsCount();
	for (size_t i = 0; i < commandcount; i++) {
		commandList[i]->_init();
	}
}

void CommandMgr::disable() {
	// do nothing
}

void CommandMgr::execute(std::u8string_view message) {
	if (GameData::getLocalPlayer() == nullptr) 
		return;

	[[maybe_unused]] auto lock = std::shared_lock(ModuleManager::moduleListMutex);

	fast_io::vector<std::u8string> cmdlist{};
	//fast_io::u8ibuffer_view a(message);
	std::u8string temp{};
	for (size_t i = 1; i < message.length(); i++) {
		if (message[i] != u8' ') [[likely]]
			temp.push_back(message[i]);
		else [[unlikely]]
			cmdlist.emplace_back(std::move(temp)); // move == swap ???
	}
	if (!temp.empty()) // the last one
		cmdlist.emplace_back(std::move(temp));  // move == swap ???

	if (!cmdlist.empty()) [[likely]] {
		std::u8string &cmdname = cmdlist.front();

		if (auto cmd = CommandMgr::NameTable.find(cmdname); cmd != CommandMgr::NameTable.end()) {
			if (cmd->second->_onExecute(cmdlist))
				Logger::LogC(LogGrade::info, u8"Command execution succeeded: ", message);
			else {
				C_GuiData::displayClientMessageC(Utils::TextColor::GRAY, u8"========================");
				C_GuiData::displayClientMessageC(fast_io::mnp::chvw(CommandMgr::prefix), Utils::TextColor::GRAY, cmd->first, Utils::TextColor::WHITE, u8" - ", Utils::TextColor::YELLOW, cmd->second->_description.at(SettingManager::languageget));
				if (!cmd->second->_usage.empty())
					C_GuiData::displayClientMessageC(Utils::TextColor::BOLD, Utils::TextColor::RED, u8"Usage: ", Utils::TextColor::RESET, Utils::TextColor::ITALIC, Utils::TextColor::GRAY, cmd->second->_usage);
				Logger::LogC(LogGrade::warning, u8"Command execution failed: ", message);
			}
		} else {
			Logger::LogC(LogGrade::warning, u8"Command not found: ", cmdname);
			if (GameData::getLocalPlayer() != nullptr) [[likely]] {  // Tell the user the command
				fast_io::vector<std::pair<std::u8string, size_t>> mean{};
				for (auto &cmds : CommandMgr::NameTable) {
					if (cmdname.size() > cmds.first.size() * 3 / 2 || cmdname.size() < cmds.first.size() * 2 / 3)  // Too much difference
						continue;
					  // Contains words
						if (cmds.first.find(cmdname) != std::u8string::npos || cmdname.find(cmds.first) != std::u8string::npos) {
							mean.emplace_back(cmds.first, cmdname.size() > cmds.first.size() ? cmdname.size() - cmds.first.size() : cmds.first.size() - cmdname.size());
							continue;
						}
						size_t wrong = dp(cmdname, cmds.first);
						if (wrong <= cmds.first.size() / 2) {
							mean.emplace_back(cmds.first, wrong);
							continue;
						}
				}
				// output

				if (mean.empty()) { // No similar
					switch (SettingManager::languageget) {
					case 0:
					default:
						C_GuiData::displayClientMessageC(
							Utils::TextColor::LIGHT_PURPLE,
							u8"[Helium] ",
							Utils::TextColor::WHITE,
							u8"Command ",
							Utils::TextColor::YELLOW,
							cmdname,
							Utils::TextColor::WHITE,
							u8" didn't exist.");
						break;
					case 1:
					case 2:
						C_GuiData::displayClientMessageC(
							Utils::TextColor::LIGHT_PURPLE,
							u8"[Helium] ",
							Utils::TextColor::WHITE,
							u8"命令 ",
							Utils::TextColor::YELLOW,
							cmdname,
							Utils::TextColor::WHITE,
							u8" 不存在。");
						break;
					case 3:
						C_GuiData::displayClientMessageC(
							Utils::TextColor::LIGHT_PURPLE,
							u8"[Helium] ",
							Utils::TextColor::WHITE,
							u8"コマンド ",
							Utils::TextColor::YELLOW,
							cmdname,
							Utils::TextColor::WHITE,
							u8" が存在しません。");
						break;
						std::unreachable();
					}
				} else {
					std::ranges::sort(mean, [](std::pair<std::u8string, size_t> &L, std::pair<std::u8string, size_t> &R) -> bool { return L.second < R.second; });
					if (mean.size() > 3) 
						mean.resize(3);

					std::u8string concatstr{};
					for (size_t i = 0; i < mean.size(); i++) {
						concatstr.append(std::move(mean[i].first)); // Note that this is MOVE! If you need to add code, please modify it!
						if (i != mean.size() - 1) [[likely]] // last one
							concatstr.append(fast_io::u8concat(Utils::TextColor::WHITE, u8", ", Utils::TextColor::YELLOW));
					}

					// didn't exist, did you mean: 
					switch (SettingManager::languageget) {
					case 0:
					default:
						C_GuiData::displayClientMessageC(
							Utils::TextColor::LIGHT_PURPLE,
							u8"[Helium] ",
							Utils::TextColor::WHITE,
							u8"Command ",
							Utils::TextColor::YELLOW,
							cmdname,
							Utils::TextColor::WHITE,
							u8" didn't exist, did you mean: ",
							Utils::TextColor::YELLOW,
							concatstr);
						break;
					case 1:
						C_GuiData::displayClientMessageC(
							Utils::TextColor::LIGHT_PURPLE,
							u8"[Helium] ",
							Utils::TextColor::WHITE,
							u8"命令 ",
							Utils::TextColor::YELLOW,
							cmdname,
							Utils::TextColor::WHITE,
							u8" 不存在，你可能想输入： ",
							Utils::TextColor::YELLOW,
							concatstr);
						break;
					case 2:
						C_GuiData::displayClientMessageC(
							Utils::TextColor::LIGHT_PURPLE,
							u8"[Helium] ",
							Utils::TextColor::WHITE,
							u8"命令 ",
							Utils::TextColor::YELLOW,
							cmdname,
							Utils::TextColor::WHITE,
							u8" 不存在，你可能想輸入： ",
							Utils::TextColor::YELLOW,
							concatstr);
						break;
					case 3:
						C_GuiData::displayClientMessageC(
							Utils::TextColor::LIGHT_PURPLE,
							u8"[Helium] ",
							Utils::TextColor::WHITE,
							u8"コマンド ",
							Utils::TextColor::YELLOW,
							cmdname,
							Utils::TextColor::WHITE,
							u8" が存在しません， ",
							Utils::TextColor::YELLOW,
							concatstr,
							Utils::TextColor::WHITE,
							u8" と入力することもできます。");
						break;
						std::unreachable();
					}

				}
			}
		}
	}
}

size_t CommandMgr::dp(const std::u8string_view x, const std::u8string_view y) {
	size_t lena = x.size();
	size_t lenb = y.size();
	//size_t *d = new size_t[lenb + 1];
	fast_io::vector<size_t> d(lenb + 1);

	size_t i, j, old, temp = 0;

	for (j = 0; j <= lenb; j++) {
		d[j] = j;
	}

	for (i = 1; i <= lena; i++) {
		old = i - 1;
		d[0] = i;
		for (j = 1; j <= lenb; j++) {
			temp = d[j];
			if (x[i - 1] == y[j - 1]) {
				d[j] = old;
			} else {
				size_t min = d[j] + 1;
				if (d[j - 1] + 1 < min)
					min = d[j - 1] + 1;
				if (old + 1 < min)
					min = old + 1;
				d[j] = min;
			}
			old = temp;
		}
	}

	return d[lenb];
}
